<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ES6函数、数组和对象</title>
</head>
<body>
  <div>1</div>
  <div>2</div>
  <div>3</div>
    <script>
    // ES6允许为函数的参数设置默认值，直接写在参数定义的后面 
    fun=(x,y='world')=>console.log(x,y)
    fun('Hello','China')
    fun('Hello','')
    fun('Hello')
    // 如果省略函数多个参数中的中间参数，用undefined来使用函数的默认值 尾参数的话就不需要
    // 参数变量是默认声明的，所以不能用let或const再次声明。
    // function foo(x = 5) {
    // const x = 1; // error
    // let x = 2; // error
    // }
    // 上面代码中，参数变量x是默认声明的，在函数体中，不能用let或const再次声明，否则会报错。
    // 使用参数默认值时，函数不能有同名参数。可以和解构复制结合起来使用

    // 函数的length属性 返回没有指定默认值的参数个数
    console.log((function (a, b, c = 5) {}).length) //2
    console.log(function (a=0,b,c){}.length)   // 0 不统计没默认值的尾参数

    //参数默认值设置为undefined，表明这个参数是可以省略的
    
    // rest函数 ...变量名用于获取函数的多余参数 搭配的是一个数组 将多余参数放入数组中
    // rest参数之后不能再有其他参数，...变量名只可以使最后一个参数
    // ES6 函数名.name会返回的是函数的名字  
    // 如果将一个具名函数赋值给一个变量，则 ES5 和 ES6 的name属性都返回这个具名函数原本的名字。
    const bar = function baz() {};
    console.log(bar.name)


//     箭头函数有几个使用注意点。

// （1）函数体内的this对象，就是定义时所在的对象，而不是使用时所在的对象。

// （2）不可以当作构造函数，也就是说，不可以使用new命令，否则会抛出一个错误。

// （3）不可以使用arguments对象，该对象在函数体内不存在。如果要用，可以用 rest 参数代替。

// （4）不可以使用yield命令，因此箭头函数不能用作 Generator 函数。
function Timer() {
  this.s1 = 0;
  this.s2 = 0;
  // 箭头函数
  setInterval(() => this.s1++, 1000);
  // 普通函数
  setInterval(function () {
    this.s2++;
  }, 1000);
}

var timer = new Timer();

setTimeout(() => console.log('s1: ', timer.s1), 3100);
setTimeout(() => console.log('s2: ', timer.s2), 3100);

//箭头函数没有自己的this 所以不能用作构造函数
//由于箭头函数没有自己的this，所以当然也就不能用call()、apply()、bind()这些方法去改变this的指向。

//尾调用是指某个函数的最后一步调用另外一个函数  尾调用不一定出现在函数尾部，只要是最后一步操作即可。
//尾调用优化
//函数调用自身，称为递归。如果尾调用自身，就称为尾递归
//就不会发生栈溢出（或者层层递归造成的超时），相对节省内存
//尾调用优化只在严格模式下开启 严格模式禁用func.arguments 和  func.caller
//try-catch命令的参数省略 

function getPoint(){
  const x = 1;
  const y = 2;
  return {x,y};
}

// 类似于Common.js输出暴露模块 module.exports = {A,B,C};

// 简写的对象方法不可以用作构造方法 会报错
const obj = {
  f(){
    this.foo = bar;
  },
  ['q'+'q']:1145085640,
  vvv(){
    return 'hi';
  }
};

//  new obj.f()//报错
// 表达式作为属性名，要将表达式放在方括号内
console.log(obj[`qq`]+obj.vvv());

// 属性名表达式与简洁表示法，不能同时使用

// 方法的name属性返回函数名（即方法名）
const person = {
    sayGoodBye(){
      console.log('hello!');
    },
    get foo() {},
    set foo(x) {}
}

console.log(person.sayGoodBye.name);
// 如果用了getter和setter方法 返回值是方法名前加get和set

// bind方法创造的函数，name属性返回bound加上原函数的名字；Function构造函数创造的函数，name属性返回anonymous。
console.log((new Function()).name + function(){}.bind.name);


// name属性返回的是这个Symbol值的描述
const key1 = Symbol('description');
const key2 = Symbol();
var obj1 = {
  [key1]() {},
  [key2]() {},
  [Symbol()]:0, 
  abc:'nice',
  mno:666
  };
console.log(obj1[key1].name+obj1[key2].name); // "[description]" 和 ""


// ES6一共有5种方法遍历对象属性
//for...in

console.log(Object.keys(obj1))

// 返回一个数组，包含对象自身的所有属性（不含 Symbol 属性，但是包括不可枚举属性）的键名
console.log(Object.getOwnPropertyNames(obj1))

// 返回一个数组 包含对象自身的Symbol属性的键名
console.log(Object.getOwnPropertySymbols(obj1))

// 返回一个数组，包含对象自身的（不含继承的）所有键名，不管键名是 Symbol 或字符串，也不管是否可枚举。
console.log(Reflect.ownKeys(obj1))

// 先遍历所有的数值键，按数值升序排列
// 其次遍历所有字符串的键，按假如时间圣墟排列
// 最后遍历所有的Symbol键，按照假如时间升序排列

// this关键字同时指向函数所在的当前对象，super指向当前对象的原型对象
let ttt={
  foo:'我是原型对象'
};
let yyy = {
  foo:'我是当前对象',
  find(){
    return super.foo
  }
};
Object.setPrototypeOf(yyy,ttt)//设置yyy原型是uuu
console.log(yyy.find())


// super关键字表示原型对象的时候，只能在对象的方法中，用在其他地方会报错
const kkk = {
  uuu:'我也是原型对象',
  foo(){
    console.log(this.uuu);
  }
}
const lll = {
  uuu:'我是当前对象',
  foo(){
    // 调用了原型对象kkk的方法 this指向了lll的lll
    super.foo();
  }
}
Object.setPrototypeOf(lll,kkk);

lll.foo()

// 结构赋值的拷贝是浅拷贝 z获取等号右边尚未读取的值 
let { x, y,...z} = { x: 1, y: 2, a: 3, b: 4 };
console.log(x+y+z);
// ...z 必须是最后一个参数 右边是undefined或者null的话则会报错
// let {...p} = null;
// let {...p}= undefined;
let fff={a:{b:666}};
let {...wtf} = fff
//fff.a.b = wtf.a.b
console.log(wtf.a.b);

let test1 = { a:999};
let test2 = { b:222};
test2._proto_ =test1;
let {...test3 } =test2
console.log(test3.b,test3.a)
// 对象o3复制了o2，但是只复制了o2自身的属性，没有复制它的原型对象o1的属性

let arr1 = ['小黄','小红','小紫'];
let arr2 = ['大绿','大橙','大粉'];
console.log(arr1.concat(arr2));
console.log([...arr1,...arr2]);
// 数组赋值给新的数组
let arr3 = [...arr1,...arr2];
console.log(arr3);

const divs = document.querySelectorAll('div');
divArr = [...divs];
// 伪数组是个对象
console.log([...divs]);
// 转化为真数组
console.log(divArr);


</script>
</body>
</html>